<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Carousel</title>
  <style>
    .carousel-container {
      max-width: 800px;
      margin: 30px auto;
      border-radius: 4px;
      box-shadow: 0 0 4px 0px rgba(0, 0, 0, 0.3);
      padding: 15px;
    }

    .carousel {
      position: relative;
      height: 200px;
      overflow: hidden;
    }

    #animation-select {
      font-size: 20px;
      margin-bottom: 12px;
    }

    .carousel .panels>a {
      position: absolute;
      display: flex;
      align-items: center;
      justify-content: center;
      width: 100%;
      height: 100%;
      text-decoration: none;
      z-index: 1;
    }

    .carousel .panels>a.active {
      z-index: 10;
    }

    .carousel .panels>a:nth-child(even) {
      background-color: lightskyblue;
    }

    .carousel .panels>a:nth-child(odd) {
      background-color: lightpink;
    }

    .carousel .arrow {
      position: absolute;
      top: 50%;
      z-index: 100;
      display: flex;
      align-items: center;
      justify-content: center;
      width: 32px;
      height: 32px;
      border: none;
      border-radius: 50%;
      background: rgba(31, 45, 61, .11);
      opacity: 0;
      transition: all .3s;
      border: none;
      cursor: pointer;
    }

    .carousel .arrow-pre {
      left: 10px;
      transform: translateX(-50%) translateY(-50%);

    }

    .carousel:hover .arrow-pre {
      transform: translateX(0) translateY(-50%);
      opacity: 1;
    }

    .carousel .arrow-next {
      right: 10px;
      transform: translateX(50%) translateY(-50%);
    }

    .carousel:hover .arrow-next {
      transform: translateX(0) translateY(-50%);
      opacity: 1;
    }

    .carousel .arrow::before {
      content: '';
      display: block;
      width: 6px;
      height: 6px;
      border-left: 1px solid #fff;
      border-top: 1px solid #fff;
      transform: rotate(-45deg);
    }

    .carousel .arrow-next::before {
      transform: rotate(135deg);
    }

    .carousel .indicators {
      position: absolute;
      left: 50%;
      bottom: 10px;
      z-index: 100;
      transform: translateX(-50%);
      list-style: none;
      margin: 0;
      padding: 0;
    }

    .carousel .indicators>li {
      display: inline-block;
      padding: 5px 0;
      cursor: pointer;
    }

    .carousel .indicators>li::before {
      content: '';
      display: block;
      width: 30px;
      height: 3px;
      border-radius: 4px;
      background: #c0c4cc;
      transition: all .3s;
    }

    .carousel .indicators>li.active::before {
      background: #fff;
    }
  </style>
</head>

<body>
  <div class="carousel-container">
    <h2>Carousel</h2>
    <div>
      <select id="animation-select">
        <option value="slide">slide</option>
        <option value="fade">fade</option>
        <option value="zoom">zoom</option>
      </select>
    </div>
    <div class="carousel">
      <div class="panels">
        <a class="active" href="#0">0</a>
        <a href="#1">1</a>
        <a href="#2">2</a>
        <a href="#3">3</a>
      </div>
      <div class="arrows">
        <button class="arrow arrow-pre"></button>
        <button class="arrow arrow-next"></button>
      </div>
      <ul class="indicators">
        <li class="active"></li>
        <li></li>
        <li></li>
        <li></li>
      </ul>
    </div>
  </div>
  <script>
    const css = ($node, cssObj) => Object.assign($node.style, cssObj)
    const Animation = {
      slide($from, $to, direction) {
        $from.style = ''
        $to.style = ''
        css($from, {
          transform: `translateX(0)`,
          zIndex: 10
        })
        css($to, {
          transform: `translateX(${direction === 'left' ? '' : '-'}100%)`,
          zIndex: 10
        })
        setTimeout(() => {
          css($from, {
            transform: `translateX(${direction === 'left' ? '-' : ''}100%)`,
            transition: `all .4s`
          })
          css($to, {
            transform: `translateX(0)`,
            transition: `all .4s`
          })
        })
      },
      fade($from, $to) {
        $from.style = ''
        $to.style = ''
        css($from, {
          zIndex: 10,
          opacity: 1
        })
        css($to, {
          zIndex: 9,
          opacity: 0
        })
        setTimeout(() => {
          css($from, {
            opacity: 0,
            zIndex: 10,
            transition: `all .4s`

          })
          css($to, {
            opacity: 1,
            zIndex: 9,
            transition: `all .4s`
          })
        })
        setTimeout(() => {
          css($from, {
            zIndex: 9
          })
          css($to, {
            zIndex: 10
          })
        }, 400)
      },
      zoom($from, $to) {
        $from.style = ''
        $to.style = ''
        css($from, {
          transform: `scale(1)`,
          opacity: 1,
          zIndex: 10
        })
        css($to, {
          transform: `scale(10)`,
          opacity: 0,
          zIndex: 9
        })
        setTimeout(() => {
          css($from, {
            transform: `scale(10)`,
            opacity: 0,
            zIndex: 10,
            transition: `all .4s`
          })
          css($to, {
            transform: `scale(1)`,
            opacity: 1,
            zIndex: 9,
            transition: `all .4s`
          })
        })
        setTimeout(() => {
          css($from, {
            zIndex: 9
          })
          css($to, {
            zIndex: 10
          })
        }, 400)
      }
    }

    class Carousel {
      constructor($root, animation) {
        this.$root = $root
        this.$pre = $root.querySelector('.arrow-pre')
        this.$next = $root.querySelector('.arrow-next')
        this.$$indicators = $root.querySelectorAll('.indicators > li')
        this.$$panels = $root.querySelectorAll('.panels > a')
        this.animation = animation
        this.bind()
      }
      bind() {
        // 可以提前绑定this
        // this.$pre.onclick = this.onPreClicked.bind(this)
        // 也可以使用箭头函数
        this.$pre.onclick = () => {
          let fromIndex = this.getIndex()
          let toIndex = this.getPreIndex()
          this.setPage(fromIndex, toIndex, 'left')
          this.setIndicators(toIndex)
        }
        this.$next.onclick = () => {
          let fromIndex = this.getIndex()
          let toIndex = this.getNextIndex()
          this.setPage(fromIndex, toIndex, 'right')
          this.setIndicators(toIndex)
        }
        this.$$indicators.forEach($indicator => {
          $indicator.onclick = (e) => {
            let fromIndex = this.getIndex()
            let toIndex = [...this.$$indicators].indexOf(e.target)
            let direction = fromIndex < toIndex ? 'left' : 'right'
            this.setIndicators(toIndex)
            this.setPage(fromIndex, toIndex, direction)
          }
        })
      }
      getIndex() {
        const $active = this.$root.querySelector('.indicators .active');
        return [...this.$$indicators].indexOf($active)
      }
      getPreIndex() {
        let index = this.getIndex()
        return (index - 1 + this.$$indicators.length) % this.$$indicators.length
      }
      getNextIndex() {
        let index = this.getIndex()
        return (index + 1) % this.$$indicators.length
      }
      setPage(fromIndex, toIndex, direction) {
        this.animation(this.$$panels[fromIndex], this.$$panels[toIndex], direction)
        // this.$$panels.forEach($panel => {
        //   $panel.classList.remove('active')
        //   this.$$panels[toIndex].classList.add('active')
        // })
      }
      setIndicators(toIndex) {
        this.$$indicators.forEach($indicator => {
          $indicator.classList.remove('active')
          this.$$indicators[toIndex].classList.add('active')
        })
      }
      setAnimation(animation) {
        this.animation = animation
      }
    }
    let $carousel = document.querySelector('.carousel')
    let carousel = new Carousel($carousel, Animation.slide)

    document.querySelector('#animation-select').onchange = function () {
      carousel.setAnimation(Animation[this.value])
    }
  </script>
</body>

</html>